// JORGE FERNANDO GOMEZ
// 1259371
// 189631
// LEC A1
// MARTIN MÜLLER
// D02
// YANG LIU


#line 10 "lander.c"

#define _POSIX_C_SOURCE 200112L

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#include <signal.h>
#include <sys/time.h>
#include <unistd.h>

#include <curses.h>
#include <math.h>
#include <errno.h>

#include "hash.h"
#include "util.h"
#include "gamestate.h"
#include "keyboardagent.h"

#include "read.h"


int main ( int argc, char * argv[] ) {

    /*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
     *       Parse command line arguments      *
     *+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*/

        opterr = 0;
        const char * optstring = "g:t:f:b:w:";
        char arg_char;

        double gravity_arg = -1.0;
        double thrust_arg = -1.0;
        FILE * file_arg = NULL;

        while ( (arg_char = getopt(argc, argv, optstring)) != -1 ) {
            if ( arg_char == '?' ) {
                printf("bad '-%c', or good option but no value.\n", optopt);
                exit(EXIT_FAILURE);
            }

            if (arg_char == 'g') {
                gravity_arg = strtod(optarg, NULL);
                if ( gravity_arg < 0.0 || gravity_arg > 20 ) {
                    printf("gravity < 0, > 20 not allowed\n");
                    exit(EXIT_FAILURE);
                }
                setGravity(gravity_arg);
            }
            else if (arg_char == 't') {
                thrust_arg = strtod(optarg, NULL);
                if ( thrust_arg > 0.0 || thrust_arg < -20 ) {
                    printf("thrust > 0, < -20 not allowed\n");
                    exit(EXIT_FAILURE);
                }
                setThrust(thrust_arg);
            }
            else if (arg_char == 'f') {
                file_arg = fopen(optarg, "r");
                if ( file_arg ==  NULL ) {
                    printf("could not open %s\n", optarg);
                    exit(EXIT_FAILURE);
                }
            }
            else if (arg_char == 'b') {
                if (optarg[0] == 't')
                    setBounce(true);
                else if (optarg[0] == 'f')
                    setBounce(false);
                else {
                    printf("-b must be either t or f\n");
                    exit(EXIT_FAILURE);
                }
            }
            else if (arg_char == 'w') {
                if (optarg[0] == 't')
                    setWrap(true);
                else if (optarg[0] == 'f')
                    setWrap(false);
                else {
                    printf("-w must be either t or f\n");
                    exit(EXIT_FAILURE);
                }
            }
        }

        if ( gravity_arg == -1.0 || thrust_arg == -1.0 || file_arg == NULL ) {
            printf("need flags -g gravity, -f landscape.txt, -t thrust\n");
            exit(EXIT_FAILURE);
        }

    /*---------------------------------------------------*/

    /*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
     *              Initialization             *
     *+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*/

        /* Open stream to sketchpad. */
        char * exec_sketchpad = "java -jar Sketchpad.jar";

        /* addStream() stores the pointer in a list for management. */
        FILE * outputStream = openStream(exec_sketchpad, 'p', "w");
        addStream(&outputStream, 'p', true);

#if GAMESTATE_DEBUG
        FILE * logStream = openLogStream("logfile", 'f', "w");
        addStream(&logStream, 'f', false);
#endif

        int inputChar;
        int init_row = 5, init_col = 10;

        initscr();

        move(init_row, init_col);
        printw("Press any key to start.");

        move(init_row + 1, init_col);
        printw("(Then press arrow keys to rotate, space for thrust, "
               "'q' to quit.)");
        refresh();

        inputChar = getch();

    /*---------------------------------------------------*/

    /*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
     *            Draw the landscape           *
     *+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*/

        struct worldLandscape landscape;
        landscape.drawCount = 0;

        /* Read file_arg into memory. */
        char * buff = (char *) malloc(MAXLEN+1);
        if ( buff == NULL )
            killim(__LINE__);
        memset(buff, '\0', (MAXLEN+1));

        /* Get input file line length. */
        for ( inputFileLength = 0;      /* Get file length (line count). */
              fgets(buff, MAXLEN+1, file_arg) != NULL;
              inputFileLength++ )
            ;
        free(buff);

        Line * Lines = initLines(inputFileLength);
        readFile(Lines, file_arg);

        char * point_x_token;
        char * point_y_token;

        double point_x;
        double point_y;

        /* Parse and draw the landscape file. */
        int currentLine;
        for ( currentLine = 0; currentLine < inputFileLength; currentLine++ ) {

            point_x_token = strtok(Lines[currentLine].lineString, " ");
            point_y_token = strtok(NULL, " ");

            point_x = strtod(point_x_token, NULL);
            point_y = strtod(point_y_token, NULL);

            if ( landscape.drawCount < MAXPOINTS ) {
                landscape.dx[landscape.drawCount] = point_x;
                landscape.dy[landscape.drawCount] = point_y;
                landscape.drawCount++;
            }
        }

        /* Store and then draw the landscape. */
        setLandscape(&landscape);
        drawLandscape();

    /*---------------------------------------------------*/

    /*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
     *           Define default input          *
     *+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*/

        setDefaultActions();

    /*---------------------------------------------------*/

    /*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
     *      Hash the action for each input     *
     *+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*/

        /* dispatchTable is the table with each key
         * representing some action the program has to take. */
        initDispatchTable();

    /*---------------------------------------------------------------*/

    /*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
     *             Create a lander             *
     *+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*/

        /* Base lander. */
        char baseLander[] = "baseLander";
        double side_length = 20.0;
        double x_offset = 130.0,  y_offset = 10.0;
        LanderFigure * baseLanderFigure =
            newLander( baseLander, side_length, x_offset, y_offset );
        eraseLander(baseLanderFigure);


        /* Actual lander. */

        /* Lander name. */
        char lander01[] = "Lander01";

        /* Lander's dimensions. */
        side_length = 20.0;

        /* Lander offset in sketchpad. */
        x_offset = 130.0,  y_offset = 10.0;

        LanderFigure * LanderA1_Figure =
                newLander( lander01, side_length, x_offset, y_offset );

        setLanderControl(LanderA1_Figure->name);

    /*---------------------------------------------------------------*/

    /*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
     *               Set the timer             *
     *+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*/

        struct itimerval timer;

        struct timeval timer_interval;
        timer_interval.tv_usec = TIMESTEP_MICRO;
        timer_interval.tv_sec = TIMESTEP_SEC;

        struct timeval timer_start;
        timer_start.tv_usec = TIMESTEP_MICRO;
        timer_start.tv_sec = TIMESTEP_SEC;

        timer.it_interval = timer_interval;
        timer.it_value = timer_start;

        setitimer(ITIMER_REAL, &timer, 0);

    /*---------------------------------------------------------------*/

    /*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
     *            Set signal handler           *
     *+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*/

        struct sigaction handleTimeStep;
        handleTimeStep.sa_handler = landerTimeStep;
        sigemptyset(&handleTimeStep.sa_mask);
        handleTimeStep.sa_flags = 0;

        sigaction(SIGALRM, &handleTimeStep, 0);

    /*---------------------------------------------------------------*/

    /*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
     *               Main Program              *
     *+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*/

    clear();
    move(init_row, 0);
    printw("left arrow key rotates counter-clockwise, right clockwise, "
           " space for thrust, q to quit.");
    refresh();
    keypad(stdscr, true);
    nodelay(stdscr, true);
    noecho();
    inputChar = 0;

    /* Run until q is pressed, or landed has crashed or landed. */
    for ( ; getPressedKey() != 'q'; )
        if ( getLandingStatus() != 0 )
            break;

    move(init_row + 2, 10);

    /* Stop the timer. */
    timer_interval.tv_usec = 0;
    timer_interval.tv_sec = 0;

    timer_start.tv_usec = 0;
    timer_start.tv_sec = 0;

    timer.it_interval = timer_interval;
    timer.it_value = timer_start;

    setitimer(ITIMER_REAL, &timer, 0);

    /* Fetch landing status and print to stdscr.
     * Also, wait for 'q' input to terminate. */
    if ( getLandingStatus() == -1 ) {
        printw("CRASHED!!!");
        nodelay(stdscr, false);
        while ( getch() != 'q' )
            ;
    }
    else if ( getLandingStatus() == 1 ) {
        printw("LANDED!!!!");
        nodelay(stdscr, false);
        while ( getch() != 'q' )
            ;
    }

    /* Clean up. */
    endwin();
    fprintf(outputStream, "end\n");
    closeStreams();

    return 0;
}
